# A sudoku puzzle solver written in python3
#
# Copyright 2009 Osman Pub
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not 
# use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software 
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
# License for the specific language governing permissions and limitations under 
# the License.
#
# Usage: python3 sudoku.py 
# and paste in the puzzle to be solved on the next nine lines (on standard 
# input), followed by an input termination keystroke (CTRL D on Unix systems) 
# to solve the puzzle (sent to standard output)
# (Examples are given at the end of the script to try)
#
# Report all bugs to osmanpub@googlemail.com
#
# This module defines a Sudoku::Puzzle class to represent a 9x9
# Sudoku puzzle and also defines exception classes raised for 
# invalid input and over-constrained puzzles. This module also defines 
# the method Sudoku.solve to solve a puzzle. The solve method uses
# the Sudoku.scan method, which is also defined here.
#
# The Sudoku::Puzzle class represents the state of a 9x9 Sudoku puzzle.
# 
# Some definitions and terminology used in this implementation: 
#
# - Each element of a puzzle is called a "cell".
# - Rows and columns are numbered from 0 to 8, and the coordinates [0,0]
#   refer to the cell in the upper-left corner of the puzzle.
# - The nine 3x3 subgrids are known as "boxes" and are also numbered from
#   0 to 8, ordered from left to right and top to bottom. The box in
#   the upper-left is box 0. The box in the upper-right is box 2. The
#   box in the middle is box 4. The box in the lower-right is box 8.
# 
# Create a new puzzle with Sudoku::Puzzle.new, specifying the initial
# state as a string or as an array of strings. The string(s) should use
# the characters 1 through 9 for the given values, and '.' for cells
# whose value is unspecified. Whitespace in the input is ignored.
#
# Read and write access to individual cells of the puzzle is through the
# [] and []= operators, which expect two-dimensional [row,column] indexing.
# These methods use numbers (not characters) 0 to 9 for cell contents.
# 0 represents an unknown value.
# 
# The has_duplicates? predicate returns true if the puzzle is invalid
# because any row, column, or box includes the same digit twice.
#
# The each_unknown method is an iterator that loops through the cells of
# the puzzle and invokes the associated block once for each cell whose
# value is unknown.
#
# The possible method returns an array of integers in the range 1..9.
# The elements of the array are the only values allowed in the specified
# cell. If this array is empty, then the puzzle is over-specified and 
# cannot be solved. If the array has only one element, then that element
# must be the value for that cell of the puzzle.

class Puzzle:
	# This is the initialization method for the class. It is automatically
	# invoked on new Puzzle instances created with Puzzle.new. Pass the input
	# puzzle as an array of lines or as a single string. Use ASCII digits 1
	# to 9 and use the '.' character for unknown cells. Whitespace, 
	# including newlines, will be stripped.
	def __init__(self,lines):
		self.lines = lines
		# These constants are used for translating between the external 
		# string representation of a puzzle and the internal representation.
		ASCII = ".123456789"
		BIN = "\000\001\002\003\004\005\006\007\010\011"
		#  # Remove whitespace (including newlines) from the data
		#  # The '!' in gsub! indicates that this is a mutator method that
		#  # alters the string directly rather than making a copy.
		#  s.gsub!(/\s/, "")  # /\s/ is a Regexp that matches any whitespace
		puzzle = ''.join(lines).replace(' ','').replace('\n','')
		#  # Raise an exception if the input is the wrong size.
		#  # Note that we use unless instead of if, and use it in modifier form.
		#  raise Invalid, "Grid is the wrong size" unless s.size == 81
		if len(puzzle) != 81:
			raise Invalid("Grid is the wrong size")
		# Check for invalid characters, and save the location of the first.
		# Note that we assign and test the value assigned at the same time.
		from re import search
		invalid = search('[^\d\.]',puzzle)
		if  invalid != None:
		  # Include the invalid character in the error message.
		  raise Invalid("Illegal character '" + str(puzzle[invalid.start()]) + \
		  "' in puzzle")
		# The following two lines convert our string of ASCII characters
		# to an array of integers, using two powerful String methods.
		# The resulting array is stored in the instance variable @grid
		# The number 0 is used to represent an unknown value.
		# Translate ASCII characters into bytes
		trans = puzzle.translate("".maketrans(ASCII, BIN)) 
		# Now unpack the bytes into an array of numbers
		self.grid =  [ord(e) for e in trans] 
		# Map box number to the index of the upper-left corner of the box.
		self.BoxToIndex = [0, 3, 6, 27, 30, 33, 54, 57, 60]
		# Make sure that the rows, columns, and boxes have no duplicates.
		if self.has_duplicates() == True:
			raise Invalid("Initial puzzle has duplicates")
		# This array holds a set of all Sudoku digits. Used below.
		self.AllDigits = [1, 2, 3, 4, 5, 6, 7, 8, 9]

	# Return the state of the puzzle as a string of 9 lines with 9 
	# characters (plus newline) each.  
	def __str__(self):
		# This method is implemented with a single line of Ruby magic that
		# reverses the steps in the initialize() method. Writing dense code
		# like this is probably not good coding style, but it demonstrates
		# the power and expressiveness of the language.
		#
		# Broken down, the line below works like this:
		# (0..8).collect invokes the code in curly braces 9 times--once
		# for each row--and collects the return value of that code into an
		# array. The code in curly braces takes a subarray of the grid
		# representing a single row and packs its numbers into a string.
		# The join() method joins the elements of the array into a single
		# string with newlines between them. Finally, the tr() method
		# translates the binary string representation into ASCII digits.
		s = ""
		for i in range(0,9):
			s = s + str(self.grid[i*9:i*9+9]).strip("[]").replace(", ",'') + '\n'
		return s

  # Return a duplicate of this Puzzle object.
  # This method overrides Object.dup to copy the @grid array.
	def dup(self):
		puzzle = Puzzle(self.lines)
		puzzle.grid = [e for e in self.grid]
		return puzzle

	# We override the array access operator to allow access to the 
	# individual cells of a puzzle. Puzzles are two-dimensional,
	# and must be indexed with row and column coordinates.
	def getitem(self,row,col):
		# Convert two-dimensional (row,col) coordinates into a one-dimensional
		# array index and get and return the cell value at that index
		return self.grid[row*9 + col]

	# This method allows the array access operator to be used on the 
	# lefthand side of an assignment operation. It sets the value of 
	# the cell at (row, col) to newvalue.
	def setitem(self,row,col,newvalue):
		# Raise an exception unless the new value is in the range 0 to 9.
		if not (newvalue in range(0,10)):
		  raise Invalid("illegal cell value")
		# Set the appropriate element of the internal array to the value.
		self.grid[row*9 + col] = newvalue

	# Returns true if any row, column, or box has duplicates.
	# Otherwise returns false. Duplicates in rows, columns, or boxes are not
	# allowed in Sudoku, so a return value of true means an invalid puzzle.
	def has_duplicates(self):
		# uniq! returns nil if all the elements in an array are unique.
		# So if uniq! returns something then the board has duplicates.
		for i in range(0,9):
			if self.uniq(self.rowdigits(i)) == False: return True
			if self.uniq(self.coldigits(i)) == False: return True
			if self.uniq(self.boxdigits(i)) == False: return True
		return False  # If all the tests have passed, then the board has no 
		# duplicates

	# Return an array of all values that could be placed in the cell 
	# at (row,col) without creating a duplicate in the row, column, or box.
	# Note that the + operator on arrays does concatenation but that the - 
	# operator performs a set difference operation.
	def possible(self,row, col, box):
		return list(set(self.AllDigits) - set(self.rowdigits(row) + \
			self.coldigits(col) + self.boxdigits(box)))

	#private  # All methods after this line are private to the class

	# Return an array of all known values in the specified row.
	def rowdigits(self,row):
		# Extract the subarray that represents the row and remove all zeros.
		# Array subtraction is set difference, with duplicate removal.
		return list(filter((lambda e: e != 0),self.grid[row*9:row*9+9]))			

	# Return an array of all known values in the specified column.
	def coldigits(self,col):
		return list(filter((lambda e: e!= 0),self.grid[col::9]))

	# Return an array of all the known values in the specified box.
	def boxdigits(self,b):
		# Convert box number to index of upper-left corner of the box.
		i = self.BoxToIndex[b]
		# Return an array of values, with 0 elements removed.
		return  list(filter((lambda e: e != 0),self.grid[i:i+3] + \
			self.grid[i+9:i+12] + self.grid[i+18:i+21]))

	def uniq(self,container):
		# the set of the container should contain only unique numbers
		# so if it's size differs from the container's size then it has duplicates
		if len(container) == len(set(container)):
			return True
		else:
			return False


# This method defines a custom looping construct (an "iterator") for
# Sudoku puzzles.  For each cell whose value is unknown, this method
# passes ("yields") the row number, column number, and box number to the 
# block associated with this iterator.
class PuzzleIter:
	def __init__(self,row,col,puzzle):
		self.row = row; self.col = col
		self.puzzle = puzzle
		# This array maps from one-dimensional grid index to box number.
		# It is used in the method below. The name BoxOfIndex begins with a 
		# capital letter, so this is a constant. Also, the array has been
		# frozen, so it cannot be modified.
		self.BoxOfIndex = [
			0,0,0,1,1,1,2,2,2,0,0,0,1,1,1,2,2,2,0,0,0,1,1,1,2,2,2,
			3,3,3,4,4,4,5,5,5,3,3,3,4,4,4,5,5,5,3,3,3,4,4,4,5,5,5,
			6,6,6,7,7,7,8,8,8,6,6,6,7,7,7,8,8,8,6,6,6,7,7,7,8,8,8
		]

	def __iter__(self):
		return self

	def next(self):
		while True:
			if self.col > 8:
				self.row += 1
				self.col = 0
			if self.row > 8:
				raise StopIteration
			index = self.row*9 + self.col
			self.col += 1
			if self.puzzle.grid[index] == 0:
				box = self.BoxOfIndex[index]  
				return (self.row,self.col - 1,box)


# An exception of this class indicates invalid input,
class Invalid(Exception):
	def __init__(self, value):
		self.value = value
	def __str__(self):
		return repr(self.value)


## An exception of this class indicates that a puzzle is over-constrained
## and that no solution is possible.
class Impossible(Exception):
	def __init__(self, value):
		self.value = value
	def __str__(self):
		return repr(self.value)


# This method scans a Puzzle, looking for unknown cells that have only
# a single possible value. If it finds any, it sets their value. Since
# setting a cell alters the possible values for other cells, it 
# continues scanning until it has scanned the entire puzzle without 
# finding any cells whose value it can set.
#
# This method returns three values. If it solves the puzzle, all three 
# values are nil. Otherwise, the first two values returned are the row and
# column of a cell whose value is still unknown. The third value is the
# set of values possible at that row and column. This is a minimal set of
# possible values: there is no unknown cell in the puzzle that has fewer
# possible values. This complex return value enables a useful heuristic 
# in the solve() method: that method can guess at values for cells where
# the guess is most likely to be correct.
# 
# This method raises Impossible if it finds a cell for which there are
# no possible values. This can happen if the puzzle is over-constrained,
# or if the solve() method below has made an incorrect guess.
#
# This method mutates the specified Puzzle object in place.
# If has_duplicates? is false on entry, then it will be false on exit.
#
def scan(puzzle):
	unchanged = False  # This is our loop variable
	# Loop until we've scanned the whole board without making a change.
	while unchanged == False:
		unchanged = True      # Assume no cells will be changed this time
		rmin = cmin = pmin = None  # Track cell with minimal possible set
		min = 10              # More than the maximal number of possibilities
		# Loop through cells whose value is unknown.
		it = PuzzleIter(0,0,puzzle)
		while True:
			try:
				row,col,box = it.next()
				# Find the set of values that could go in this cell
				p = puzzle.possible(row, col, box)
				# Branch based on the size of the set p. 
				# We care about 3 cases: p.size==0, p.size==1, and p.size > 1.
				psize = len(p)
				if psize == 0:
				# No possible values means the puzzle is over-constrained
				  raise Impossible("over constrained puzzle")
				elif psize == 1:  # We've found a unique value, so set it in the grid
				  puzzle.setitem(row,col,p[0]) # Set that position on the grid to the 
				  # value
				  unchanged = False      # Note that we've made a change
				else:    # For any other number of possibilities
				  # Keep track of the smallest set of possibilities.
				  # But don't bother if we're going to repeat this loop.
				  if (unchanged == True) and (psize < min):
				    min = psize                    # Current smallest size
				    rmin, cmin, pmin = row, col, p  # Note parallel assignment
			except StopIteration:
				break
	# Return the cell with the minimal set of possibilities.
	# Note multiple return values.
	return (rmin, cmin, pmin)


# Solve a Sudoku puzzle using simple logic, if possible, but fall back
# on brute-force when necessary. This is a recursive method. It either
# returns a solution or raises an exception. The solution is returned
# as a new Puzzle object with no unknown cells. This method does not 
# modify the Puzzle it is passed. Note that this method cannot detect
# an under-constrained puzzle.
def solve(puzzle):
	# Make a private copy of the puzzle that we can modify.
	puzzle_copy = puzzle.dup()
	# Use logic to fill in as much of the puzzle as we can.
	# This method mutates the puzzle we give it, but always leaves it valid.
	# It returns a row, a column, and set of possible values at that cell.
	# Note parallel assignment of these return values to three variables.
	r,c,p = scan(puzzle_copy)
	# If we solved it with logic, return the solved puzzle.
	if r == None: return puzzle_copy

	# Otherwise, try each of the values in p for cell [r,c].
	# Since we're picking from a set of possible values, the guess leaves
	# the puzzle in a valid state. The guess will either lead to a solution
	# or to an impossible puzzle. We'll know we have an impossible
	# puzzle if a recursive call to scan throws an exception. If this happens
	# we need to try another guess, or re-raise an exception if we've tried
	# all the options we've got.
	for guess in p:
		puzzle_copy.setitem(r,c,guess)
    # Now try (recursively) to solve the modified puzzle.
    # This recursive invocation will call scan() again to apply logic
    # to the modified board, and will then guess another cell if needed.
    # Remember that solve() will either return a valid solution or 
    # raise an exception.  
		try:
			return solve(puzzle_copy)
		except Impossible:
			continue
	## If we get here, then none of our guesses worked out
	## so we must have guessed wrong sometime earlier.
	raise Impossible("try again!")


if __name__ == "__main__":
	from sys import stdin
	puzzle = []
	for line in stdin:
		puzzle += line
	print(solve(Puzzle(puzzle)))

"""
.256..49.
7...2....
.84.376..
5.9....3.
1.23.58.9
.3....2.4
..385.92.
....9...3
.91..357.

53..7....
6..195...
.98....6.
8...6...3
4..8.3..1
7...2...6
.6....28.
...419..5
....8..79

..53.694.
.3.1....6
.......3.
7..9.....
.1..3..2.
.....2..7
.6.......
8....7.5.
.436.81..
"""
